package se.jocke.nbrs.ui.window;

import com.google.common.eventbus.Subscribe;
import java.awt.Color;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JEditorPane;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.EditorKit;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.settings.ConvertAsProperties;
import org.netbeans.editor.BaseDocument;
import org.netbeans.modules.editor.indent.api.Reformat;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.cookies.OpenCookie;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.loaders.DataObject;
import org.openide.loaders.DataObjectNotFoundException;
import org.openide.nodes.Node;
import org.openide.text.CloneableEditorSupport;
import org.openide.util.Exceptions;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import se.jocke.nbrs.model.RestService;
import se.jocke.nbrs.model.RestServiceGroup;
import se.jocke.nbrs.model.RestServiceResponse;
import se.jocke.nbrs.service.EventBus;
import se.jocke.nbrs.service.RestServiceManager;
import se.jocke.nbrs.service.event.RestRequestFailed;
import se.jocke.nbrs.service.event.RestResponseEvent;
import se.jocke.nbrs.service.event.SendRequestEvent;
import se.jocke.nbrs.ui.node.RSGroupNode;
import se.jocke.nbrs.ui.node.RSNode;

/**
 * Top component which displays something.
 */
@ConvertAsProperties(
        dtd = "-//se.jocke.nbrs.ui.window//RestServiceEditor//EN",
        autostore = false
)
@TopComponent.Description(
        preferredID = RestServiceEditorTopComponent.PREF_ID,
        //iconBase="SET/PATH/TO/ICON/HERE", 
        persistenceType = TopComponent.PERSISTENCE_ALWAYS
)
@TopComponent.Registration(mode = "editor", openAtStartup = false)
@ActionID(category = "Window", id = "se.jocke.nbrs.ui.window.RestServiceEditorTopComponent")
@ActionReference(path = "Menu/Window" /*, position = 333 */)
@TopComponent.OpenActionRegistration(
        displayName = "#CTL_RestServiceEditorAction",
        preferredID = RestServiceEditorTopComponent.PREF_ID
)
@Messages({
    "CTL_RestServiceEditorAction=Rest Service Editor",
    "CTL_RestServiceEditorTopComponent=Rest Service Editor Window",
    "HINT_RestServiceEditorTopComponent=Rest Service Editor window"
})
public final class RestServiceEditorTopComponent extends TopComponent {

    public static final String PREF_ID = "RestServiceEditorTopComponent";

    private static final long serialVersionUID = 1L;

    private Lookup.Result<RestService> lookupResult;

    private static final Map<String, String> CONTENT_TYPE_TO_EDITOR_MAP = new ConcurrentHashMap<String, String>();

    private static final Map<String, String> CONTENT_TYPE_TO_FILE_EXT = new ConcurrentHashMap<String, String>();

    private final Map<RestService, ProgressHandle> requestsProgressMap = new IdentityHashMap<RestService, ProgressHandle>();

    private final String[] contentTypes = new String[]{
        "application/atom+xml",
        "application/x-www-form-urlencoded",
        "application/json",
        "application/octet-stream",
        "application/svg+xml",
        "application/xhtml+xml",
        "application/xml",
        "text/html",
        "text/plain",
        "text/xml",
        "*/*"};

    private final LookupListener lookupListener = new LookupListener() {
        @Override
        public void resultChanged(LookupEvent ev) {
            @SuppressWarnings("unchecked")
            Lookup.Result<RestService> r = (Lookup.Result) ev.getSource();
            if (!r.allInstances().isEmpty()) {
                updateRestService(r.allInstances().iterator().next());
            }
        }
    };

    static {
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/atom+xml", "text/xml");
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/x-www-form-urlencoded", "text/html");
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/json", "text/x-json");
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/octet-stream", "text/plain");
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/svg+xml", "text/xml");
        CONTENT_TYPE_TO_EDITOR_MAP.put("text/html", "text/html");
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/xhtml+xml", "text/html");
        CONTENT_TYPE_TO_EDITOR_MAP.put("application/xml", "text/xml");
        CONTENT_TYPE_TO_EDITOR_MAP.put("text/plain", "text/plain");
    }

    static {
        CONTENT_TYPE_TO_FILE_EXT.put("text/xml", "xml");
        CONTENT_TYPE_TO_FILE_EXT.put("text/html", "html");
        CONTENT_TYPE_TO_FILE_EXT.put("text/x-json", "json");
        CONTENT_TYPE_TO_FILE_EXT.put("text/plain", "txt");
    }

    public RestServiceEditorTopComponent() {
        initComponents();
        BaseDocument document = new BaseDocument(false, "text/plain");
        responseEditorPane.setDocument(document);
        setName(Bundle.CTL_RestServiceEditorTopComponent());
        setToolTipText(Bundle.HINT_RestServiceEditorTopComponent());
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        responseEditorPopupMenu = new javax.swing.JPopupMenu();
        openEditorMenuItem = new javax.swing.JMenuItem();
        jLabel1 = new javax.swing.JLabel();
        nameTF = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();
        methodComboBox = new javax.swing.JComboBox();
        jLabel3 = new javax.swing.JLabel();
        jTabbedPane1 = new javax.swing.JTabbedPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        requestEditorPane = new javax.swing.JEditorPane();
        jPanel3 = new javax.swing.JPanel();
        jScrollPane4 = new javax.swing.JScrollPane();
        parameterTable = new javax.swing.JTable();
        jPanel4 = new javax.swing.JPanel();
        jScrollPane2 = new javax.swing.JScrollPane();
        requestHeadersTable = new javax.swing.JTable();
        jPanel5 = new javax.swing.JPanel();
        jLabel6 = new javax.swing.JLabel();
        authNameTextField = new javax.swing.JTextField();
        jLabel7 = new javax.swing.JLabel();
        passwordField = new javax.swing.JPasswordField();
        contentTypeComboBox = new javax.swing.JComboBox();
        jTabbedPane2 = new javax.swing.JTabbedPane();
        jScrollPane3 = new javax.swing.JScrollPane();
        responseEditorPane = new javax.swing.JEditorPane();
        jScrollPane5 = new javax.swing.JScrollPane();
        responseHeaderTable = new javax.swing.JTable();
        jScrollPane6 = new javax.swing.JScrollPane();
        cookieTable = new javax.swing.JTable();
        sendButton = new javax.swing.JButton();
        pathTF = new javax.swing.JTextField();
        hostComboBox = new javax.swing.JComboBox();
        saveButton = new javax.swing.JButton();
        addHostButton = new javax.swing.JButton();
        removeHostButton = new javax.swing.JButton();
        acceptComboBox = new javax.swing.JComboBox();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        statusLabel = new javax.swing.JLabel();

        responseEditorPopupMenu.setMinimumSize(new java.awt.Dimension(200, 200));

        org.openide.awt.Mnemonics.setLocalizedText(openEditorMenuItem, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.openEditorMenuItem.text")); // NOI18N
        openEditorMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                openEditorMenuItemActionPerformed(evt);
            }
        });
        responseEditorPopupMenu.add(openEditorMenuItem);

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel1.text")); // NOI18N

        nameTF.setText(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.nameTF.text")); // NOI18N
        nameTF.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                nameTFActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel2.text")); // NOI18N

        methodComboBox.setEditable(true);
        methodComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "GET", "POST", "PUT", "PATCH", "DELETE", "COPY", "HEAD", "OPTIONS", "LINK", "UNLINK", "PURGE" }));

        org.openide.awt.Mnemonics.setLocalizedText(jLabel3, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel3.text")); // NOI18N

        jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
        jScrollPane1.setViewportView(requestEditorPane);

        jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jScrollPane1.TabConstraints.tabTitle"), jScrollPane1); // NOI18N

        jScrollPane4.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

        parameterTable.setAutoCreateRowSorter(true);
        parameterTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null}
            },
            new String [] {
                "Name", "Value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        parameterTable.setColumnSelectionAllowed(true);
        jScrollPane4.setViewportView(parameterTable);
        parameterTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        if (parameterTable.getColumnModel().getColumnCount() > 0) {
            parameterTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.parameterTable.columnModel.title0")); // NOI18N
            parameterTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.parameterTable.columnModel.title1")); // NOI18N
        }

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane4, javax.swing.GroupLayout.DEFAULT_SIZE, 885, Short.MAX_VALUE)
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jScrollPane4, javax.swing.GroupLayout.PREFERRED_SIZE, 212, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jPanel3.TabConstraints.tabTitle"), jPanel3); // NOI18N

        jScrollPane2.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

        requestHeadersTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null}
            },
            new String [] {
                "Name", "Value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        requestHeadersTable.setColumnSelectionAllowed(true);
        jScrollPane2.setViewportView(requestHeadersTable);
        requestHeadersTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        if (requestHeadersTable.getColumnModel().getColumnCount() > 0) {
            requestHeadersTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.requestHeadersTable.columnModel.title0")); // NOI18N
            requestHeadersTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.requestHeadersTable.columnModel.title1")); // NOI18N
        }

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 885, Short.MAX_VALUE)
            .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 885, Short.MAX_VALUE))
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 174, Short.MAX_VALUE)
            .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 174, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jPanel4.TabConstraints.tabTitle"), jPanel4); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel6.text")); // NOI18N

        authNameTextField.setText(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.authNameTextField.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jLabel7, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel7.text")); // NOI18N

        passwordField.setText(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.passwordField.text")); // NOI18N

        javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
        jPanel5.setLayout(jPanel5Layout);
        jPanel5Layout.setHorizontalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel5Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jLabel6)
                    .addComponent(authNameTextField)
                    .addComponent(jLabel7)
                    .addComponent(passwordField, javax.swing.GroupLayout.DEFAULT_SIZE, 561, Short.MAX_VALUE))
                .addContainerGap(312, Short.MAX_VALUE))
        );
        jPanel5Layout.setVerticalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel5Layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel6)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(authNameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jLabel7)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(passwordField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(50, Short.MAX_VALUE))
        );

        jTabbedPane1.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jPanel5.TabConstraints.tabTitle"), jPanel5); // NOI18N

        contentTypeComboBox.setModel(new javax.swing.DefaultComboBoxModel(contentTypes));
        contentTypeComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                contentTypeChanged(evt);
            }
        });

        responseEditorPane.setComponentPopupMenu(responseEditorPopupMenu);
        jScrollPane3.setViewportView(responseEditorPane);

        jTabbedPane2.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jScrollPane3.TabConstraints.tabTitle"), jScrollPane3); // NOI18N

        responseHeaderTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null}
            },
            new String [] {
                "Name", "Value"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        responseHeaderTable.setColumnSelectionAllowed(true);
        jScrollPane5.setViewportView(responseHeaderTable);
        responseHeaderTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        if (responseHeaderTable.getColumnModel().getColumnCount() > 0) {
            responseHeaderTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.responseHeaderTable.columnModel.title0")); // NOI18N
            responseHeaderTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.responseHeaderTable.columnModel.title1")); // NOI18N
        }

        jTabbedPane2.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jScrollPane5.TabConstraints.tabTitle"), jScrollPane5); // NOI18N

        cookieTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null},
                {null, null}
            },
            new String [] {
                "Name", "Value"
            }
        ));
        cookieTable.setColumnSelectionAllowed(true);
        jScrollPane6.setViewportView(cookieTable);
        cookieTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        if (cookieTable.getColumnModel().getColumnCount() > 0) {
            cookieTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.cookieTable.columnModel.title0")); // NOI18N
            cookieTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.cookieTable.columnModel.title1")); // NOI18N
        }

        jTabbedPane2.addTab(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jScrollPane6.TabConstraints.tabTitle"), jScrollPane6); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(sendButton, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.sendButton.text")); // NOI18N
        sendButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                sendButtonActionPerformed(evt);
            }
        });

        pathTF.setText(org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.pathTF.text")); // NOI18N

        hostComboBox.setEditable(true);
        hostComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "http://localhost" }));
        hostComboBox.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                hostComboBoxActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(saveButton, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.saveButton.text")); // NOI18N
        saveButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                saveButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(addHostButton, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.addHostButton.text")); // NOI18N
        addHostButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addHostButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(removeHostButton, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.removeHostButton.text")); // NOI18N
        removeHostButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeHostButtonActionPerformed(evt);
            }
        });

        acceptComboBox.setModel(new javax.swing.DefaultComboBoxModel(contentTypes));

        org.openide.awt.Mnemonics.setLocalizedText(jLabel4, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel4.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jLabel5, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.jLabel5.text")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(statusLabel, org.openide.util.NbBundle.getMessage(RestServiceEditorTopComponent.class, "RestServiceEditorTopComponent.statusLabel.text")); // NOI18N

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTabbedPane1)
                    .addComponent(jTabbedPane2)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(methodComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 141, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel3))
                        .addGap(18, 18, 18)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(hostComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 381, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(addHostButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(removeHostButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(pathTF))
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(jLabel2)
                                .addGap(0, 0, Short.MAX_VALUE))))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addComponent(nameTF, javax.swing.GroupLayout.PREFERRED_SIZE, 276, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(contentTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 182, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jLabel4))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(jLabel5)
                                    .addComponent(acceptComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 182, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addGroup(layout.createSequentialGroup()
                                .addGap(6, 6, 6)
                                .addComponent(saveButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(sendButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(statusLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 439, javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addGap(0, 0, Short.MAX_VALUE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(nameTF, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel2)
                    .addComponent(jLabel3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(methodComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(pathTF)
                    .addComponent(hostComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(addHostButton)
                    .addComponent(removeHostButton))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(jLabel5))
                .addGap(1, 1, 1)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(contentTypeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(acceptComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(18, 18, 18)
                .addComponent(jTabbedPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 211, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
                    .addComponent(statusLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 17, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(sendButton)
                    .addComponent(saveButton))
                .addGap(18, 18, 18)
                .addComponent(jTabbedPane2)
                .addContainerGap())
        );
    }// </editor-fold>//GEN-END:initComponents

    private void nameTFActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameTFActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_nameTFActionPerformed

    private void contentTypeChanged(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_contentTypeChanged
        setEditorKit(requestEditorPane, (String) contentTypeComboBox.getSelectedItem());
    }//GEN-LAST:event_contentTypeChanged

    private void sendButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sendButtonActionPerformed
        ProgressHandle handle = ProgressHandleFactory.createHandle("Sending request to " + hostComboBox.getSelectedItem());

        statusLabel.setText("");
        statusLabel.setForeground(null);
        clearTableModel(responseHeaderTable.getModel());
        clearTableModel(cookieTable.getModel());
        responseEditorPane.setText("");

        handle.start();

        RestService rs = new RestService();
        rs.setName(nameTF.getText());
        rs.setHosts(Collections.singletonList((String) hostComboBox.getSelectedItem()));
        rs.setMethod((String) methodComboBox.getSelectedItem());
        rs.setBody(requestEditorPane.getText());
        rs.setContentType((String) contentTypeComboBox.getSelectedItem());
        rs.setPath(pathTF.getText());
        rs.setHeaders(modelToMap(requestHeadersTable.getModel()));
        rs.setParameters(modelToMap(parameterTable.getModel()));
        rs.setAccept((String) acceptComboBox.getSelectedItem());
        rs.setUserName(authNameTextField.getText());
        rs.setPassword(new String(passwordField.getPassword())); //Clear text I Know

        requestsProgressMap.put(rs, handle);

        Lookup.getDefault().lookup(EventBus.class).postAsync(new SendRequestEvent(rs));

    }//GEN-LAST:event_sendButtonActionPerformed

    private void saveButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveButtonActionPerformed
        RestServiceTreeTopComponent tree = (RestServiceTreeTopComponent) WindowManager.getDefault().findTopComponent(RestServiceTreeTopComponent.PREF_ID);

        Node[] selectedNodes = tree.getExplorerManager().getSelectedNodes();

        if (selectedNodes.length == 1 && (selectedNodes[0] instanceof RSNode)) {

            RSNode node = (RSNode) selectedNodes[0];

            RSGroupNode groupNode = (RSGroupNode) node.getParentNode();

            RestServiceGroup group = groupNode.getGroup();

            RestService rs = new RestService();

            List<String> hosts = new ArrayList<String>(hostComboBox.getItemCount());
            rs.setName(nameTF.getText());
            for (int i = 0; i < hostComboBox.getItemCount(); i++) {
                hosts.add((String) hostComboBox.getItemAt(i));
            }

            rs.setHosts(hosts);
            rs.setMethod((String) methodComboBox.getSelectedItem());
            rs.setBody(requestEditorPane.getText());
            rs.setContentType((String) contentTypeComboBox.getSelectedItem());
            rs.setPath(pathTF.getText());
            rs.setHeaders(modelToMap(requestHeadersTable.getModel()));
            rs.setParameters(modelToMap(parameterTable.getModel()));
            rs.setAccept((String) acceptComboBox.getSelectedItem());
            rs.setUserName(authNameTextField.getText());
            rs.setPassword(new String(passwordField.getPassword())); //Clear text I Know
            Lookup.getDefault().lookup(RestServiceManager.class).saveRestService(group, rs);
        } else {
            LOG.log(Level.INFO, "No node selected");
        }

    }//GEN-LAST:event_saveButtonActionPerformed

    @SuppressWarnings("unchecked")
    private void addHostButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addHostButtonActionPerformed
        hostComboBox.addItem("http://");
    }//GEN-LAST:event_addHostButtonActionPerformed

    private void removeHostButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeHostButtonActionPerformed
        hostComboBox.removeItemAt(hostComboBox.getSelectedIndex());
    }//GEN-LAST:event_removeHostButtonActionPerformed

    @SuppressWarnings("unchecked")
    private void hostComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hostComboBoxActionPerformed
        if (evt.getActionCommand().equals("comboBoxEdited")) {
            hostComboBox.insertItemAt(hostComboBox.getSelectedItem(), 0);
        }
    }//GEN-LAST:event_hostComboBoxActionPerformed

    private void openEditorMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openEditorMenuItemActionPerformed

        FileLock lock = null;
        OutputStream outputStream = null;
        FileObject fob = null;

        String contentType = responseEditorPane.getContentType();
        String ext = CONTENT_TYPE_TO_FILE_EXT.containsKey(contentType) ? CONTENT_TYPE_TO_FILE_EXT.get(contentType) : "txt";

        try {
            fob = FileUtil.createMemoryFileSystem().getRoot().createData("response", ext);

            lock = fob.lock();
            outputStream = fob.getOutputStream(lock);

            outputStream.write(responseEditorPane.getText().getBytes());
            outputStream.flush();

        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        } finally {

            if (lock != null) {
                lock.releaseLock();
            }

            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException ex) {
                }
            }

        }

        if (fob != null) {

            DataObject data;
            try {
                data = DataObject.find(fob);
                OpenCookie cookie = data.getLookup().lookup(OpenCookie.class);
                cookie.open();
            } catch (DataObjectNotFoundException ex) {
                Exceptions.printStackTrace(ex);
            }
        }

    }//GEN-LAST:event_openEditorMenuItemActionPerformed

    @SuppressWarnings("unchecked")
    private void updateRestService(RestService service) {
        nameTF.setText(service.getName());

        if (service.getContentType() != null) {
            contentTypeComboBox.setSelectedItem(service.getContentType());
            setEditorKit(requestEditorPane, (String) contentTypeComboBox.getSelectedItem());

        } else {
            contentTypeComboBox.setSelectedIndex(0);
        }

        hostComboBox.removeAllItems();

        if (!service.getHosts().isEmpty()) {
            List<String> hosts = service.getHosts();
            for (String url : hosts) {
                hostComboBox.addItem(url);
            }
        }

        if (service.getPath() != null) {
            pathTF.setText(service.getPath());
        }

        if (service.getBody() != null) {
            requestEditorPane.setText(service.getBody());
        }

        if (service.getMethod() != null) {
            methodComboBox.setSelectedItem(service.getMethod());
        }

        if (service.getAccept() != null) {
            acceptComboBox.setSelectedItem(service.getAccept());
        }

        clearTableModel(requestHeadersTable.getModel());

        if (service.getHeaders() != null) {
            updateTableModel(service.getHeaders(), requestHeadersTable.getModel());
        }

        clearTableModel(parameterTable.getModel());

        if (service.getParameters() != null) {
            updateTableModel(service.getParameters(), requestHeadersTable.getModel());
        }

        if (service.getParameters() != null) {
            updateTableModel(service.getParameters(), parameterTable.getModel());
        }

        if (service.getUserName() != null) {
            authNameTextField.setText(service.getUserName());
        }

        if (service.getPassword() != null) {
            passwordField.setText(service.getPassword());
        }

    }

    @Subscribe
    public void onRestRequestFailed(RestRequestFailed requestFailed) {

        ProgressHandle handle = requestsProgressMap.remove(requestFailed.getRestService());

        if (handle != null) {
            handle.finish();
        }

        Exceptions.printStackTrace(requestFailed.getException());
    }

    @Subscribe
    public void onRestResponseEvent(RestResponseEvent responseEvt) {

        RestServiceResponse response = responseEvt.getRsp();

        ProgressHandle handle = requestsProgressMap.remove(responseEvt.getRestService());

        if (handle != null) {
            handle.finish();
        }

        statusLabel.setText(response.getResponseCode() + " : " + response.getResponseLine());

        if (response.getResponseCode() < 300) {
            statusLabel.setForeground(Color.GREEN);
        } else if (response.getResponseCode() < 400) {
            statusLabel.setForeground(Color.YELLOW);
        } else {
            statusLabel.setForeground(Color.RED);
        }

        setEditorKit(responseEditorPane, response.getContentType());

        clearTableModel(responseHeaderTable.getModel());
        updateTableModel(response.getHeaders(), responseHeaderTable.getModel());

        clearTableModel(cookieTable.getModel());
        updateTableModel(response.getCookies(), cookieTable.getModel());

        responseEditorPane.setText(response.getBody());

        final BaseDocument baseDoc = (BaseDocument) responseEditorPane.getDocument();

        final Reformat reformat = Reformat.get(baseDoc);

        reformat.lock();

        try {
            baseDoc.runAtomic(new Runnable() {
                @Override
                public void run() {
                    try {
                        reformat.reformat(0, baseDoc.getLength());
                    } catch (BadLocationException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            });

        } finally {
            reformat.unlock();
        }

    }

    private void setEditorKit(JEditorPane editorPane, String contentType) {
        if (CONTENT_TYPE_TO_EDITOR_MAP.containsKey(contentType)) {
            EditorKit kit = CloneableEditorSupport.getEditorKit(CONTENT_TYPE_TO_EDITOR_MAP.get(contentType));
            editorPane.setEditorKit(kit);
        } else {
            LOG.log(Level.WARNING, "No editor found for {0} ", contentType);
        }
    }
    private static final Logger LOG = Logger.getLogger(RestServiceEditorTopComponent.class.getName());

    private void updateTableModel(Map<String, String> headers, TableModel model) {
        int i = 0;

        for (Map.Entry<String, String> entry : headers.entrySet()) {
            model.setValueAt(entry.getKey(), i, 0);
            model.setValueAt(entry.getValue(), i, 1);
            i++;
        }
    }

    private void clearTableModel(TableModel model) {
        for (int i = 0; i < model.getRowCount(); i++) {
            model.setValueAt(null, i, 0);
            model.setValueAt(null, i, 1);
        }
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JComboBox acceptComboBox;
    private javax.swing.JButton addHostButton;
    private javax.swing.JTextField authNameTextField;
    private javax.swing.JComboBox contentTypeComboBox;
    private javax.swing.JTable cookieTable;
    private javax.swing.JComboBox hostComboBox;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JScrollPane jScrollPane3;
    private javax.swing.JScrollPane jScrollPane4;
    private javax.swing.JScrollPane jScrollPane5;
    private javax.swing.JScrollPane jScrollPane6;
    private javax.swing.JTabbedPane jTabbedPane1;
    private javax.swing.JTabbedPane jTabbedPane2;
    private javax.swing.JComboBox methodComboBox;
    private javax.swing.JTextField nameTF;
    private javax.swing.JMenuItem openEditorMenuItem;
    private javax.swing.JTable parameterTable;
    private javax.swing.JPasswordField passwordField;
    private javax.swing.JTextField pathTF;
    private javax.swing.JButton removeHostButton;
    private javax.swing.JEditorPane requestEditorPane;
    private javax.swing.JTable requestHeadersTable;
    private javax.swing.JEditorPane responseEditorPane;
    private javax.swing.JPopupMenu responseEditorPopupMenu;
    private javax.swing.JTable responseHeaderTable;
    private javax.swing.JButton saveButton;
    private javax.swing.JButton sendButton;
    private javax.swing.JLabel statusLabel;
    // End of variables declaration//GEN-END:variables

    @Override
    public void componentOpened() {
        TopComponent tree = WindowManager.getDefault().findTopComponent(RestServiceTreeTopComponent.PREF_ID);
        if (tree != null) {
            lookupResult = tree.getLookup().lookupResult(RestService.class);
            lookupResult.addLookupListener(lookupListener);
        }

        Lookup.getDefault().lookup(EventBus.class).register(this);
    }

    @Override
    public void componentClosed() {
        if (lookupResult != null) {
            lookupResult.removeLookupListener(lookupListener);
        }
        Lookup.getDefault().lookup(EventBus.class).unregister(this);
    }

    void writeProperties(java.util.Properties p) {
        p.setProperty("version", "1.0");
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
    }

    private Map<String, String> modelToMap(TableModel model) {
        Map<String, String> map = new HashMap<String, String>();
        for (int i = 0; i < model.getRowCount(); i++) {
            if (model.getValueAt(i, 0) != null && model.getValueAt(i, 1) != null) {
                map.put((String) model.getValueAt(i, 0), (String) model.getValueAt(i, 1));
            }
        }
        return map;
    }
}
